﻿Namespace GameState
  Public MustInherit Class AbstractGameState

    ''' <summary>
    ''' make a clone of the given gamestate
    ''' </summary>
    Friend Sub CopyDataFrom(ByVal source As AbstractGameState)
      stateFirstCard = source.FirstCard
      stateSecondCard = source.SecondCard
      statePlayersList = source.PlayersList
      stateCardsList = source.CardsList
    End Sub

    ''' <summary>
    ''' do a turn using the selected card
    ''' </summary>
    Friend Overridable Sub DoTurn(ByVal card As Card)
    End Sub

    ''' <summary>
    ''' called if this state has become active
    ''' </summary>
    Friend Overridable Sub OnActivate()
    End Sub

    Protected advicedGameState As AbstractGameState
    ''' <summary>
    ''' after DoTurn, this property contains the adviced game state
    ''' </summary>
    Friend ReadOnly Property NextGameState() As AbstractGameState
      Get
        Return advicedGameState
      End Get
    End Property

    ''' <summary>
    ''' fires whenever a new advice on the next gamestate is available
    ''' </summary>
    Public Event NewAdviceAvailable(ByVal sender As Object, ByVal e As EventArgs)

    ''' <summary>
    ''' advice as new gamestate the given gamestate
    ''' </summary>
    ''' <typeparam name="T">type of new gamestate</typeparam>
    Protected Sub adviceGameState(Of T As {AbstractGameState, New})()
      advicedGameState = New T
      advicedGameState.CopyDataFrom(Me)
      RaiseEvent NewAdviceAvailable(Me, New EventArgs)
    End Sub

    Protected stateFirstCard As Card
    ''' <summary>
    ''' the card that is turned as the first card
    ''' </summary>
    Public Property FirstCard() As Card
      Get
        Return stateFirstCard
      End Get
      Set(ByVal value As Card)
        stateFirstCard = value
      End Set
    End Property

    Protected stateSecondCard As Card
    ''' <summary>
    ''' the card that is turned secondly
    ''' </summary>
    Public Property SecondCard() As Card
      Get
        Return stateSecondCard
      End Get
      Set(ByVal value As Card)
        stateSecondCard = value
      End Set
    End Property

#Region "PlayerList Functions"

    Protected statePlayersList As IDictionary(Of Integer, Player)
    ''' <summary>
    ''' list of players and the state of each player
    ''' </summary>
    Public Property PlayersList() As IDictionary(Of Integer, Player)
      Get
        Return statePlayersList
      End Get
      Set(ByVal value As IDictionary(Of Integer, Player))
        statePlayersList = value
      End Set
    End Property

    ''' <summary>
    ''' create a new (numbered) players list from the flat list
    ''' </summary>
    Public Sub CopyPlayersListFrom(ByVal source As IList(Of Player))
      If source Is Nothing OrElse source.Count = 0 Then
        Throw New ArgumentNullException("PlayerList not set")
      End If
      statePlayersList = New Dictionary(Of Integer, Player)
      For Each player As Player In source
        player.InternalId = statePlayersList.Count
        statePlayersList.Add(player.InternalId, player)
      Next
    End Sub

    ''' <summary>
    ''' the first player 
    ''' </summary>
    Public Function FirstPlayer() As Player
      Return statePlayersList(0)
    End Function

    ''' <summary>
    ''' find the player that makes a move
    ''' </summary>
    ''' <returns>The active player or Null if all players are inactive.</returns>
    Friend Function MovingPlayer() As Player
      For Each player As Player In statePlayersList.Values
        Select Case player.TurnState
          Case player.TurnStateE.MayMakeFirstMove, player.TurnStateE.MayMakeSecondMove, player.TurnStateE.WinsTurn, player.TurnStateE.LoosesTurn
            Return player
          Case player.TurnStateE.Inactive
            'not this player
        End Select
      Next
      Return Nothing
    End Function

    ''' <summary>
    ''' select the next player to pass the turn to
    ''' </summary>
    Friend Function NextPlayer(ByVal lastPlayer As Player) As Player
      Dim newIndex As Integer = lastPlayer.InternalId + 1
      If newIndex = statePlayersList.Count Then
        newIndex = 0
      End If
      Return statePlayersList(newIndex)
    End Function

    ''' <summary>
    ''' the winning player(s)
    ''' </summary>
    Public Function WinningPlayers() As IList(Of Player)
      Dim sortedPlayers = From player In statePlayersList.Values Order By player.Score Descending
      Dim winning As New List(Of Player)
      Dim winningScore = sortedPlayers.First.Score
      'there can be multiple players with the same (winning) score
      For Each player In sortedPlayers
        If player.Score = winningScore Then
          winning.Add(player)
        End If
      Next
      Return winning
    End Function
#End Region

#Region "CardsList functions"
    Private stateCardsList As IList(Of Card) = New List(Of Card)
    ''' <summary>
    ''' list of cards still in the game
    ''' </summary>
    Public ReadOnly Property CardsList() As IList(Of Card)
      Get
        Return stateCardsList
      End Get
    End Property

    ''' <summary>
    ''' receive list of cards for this game. The cards should be unique.
    ''' </summary>
    Public Sub InitializeCards(ByVal uniqueCardsList As IList(Of Card))
      'make a copy
      stateCardsList = New List(Of Card)(uniqueCardsList)

      'check validness
      If stateCardsList Is Nothing OrElse stateCardsList.Count = 0 Then
        Throw New ArgumentNullException("Cardlist not set to a valid value")
      End If

      'make twin pairs from the unique cards
      makeTwins()

      'shuffle the cards
      stateCardsList = New Shuffler(Of Card)().Shuffle(stateCardsList)
    End Sub


    ''' <summary>
    ''' initialize the card collection with the twin cards.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub makeTwins()

      'iterate over a copy of the list because we change it
      For Each card As Card In New List(Of Card)(CardsList)
        If card.TwinCard IsNot Nothing Then
          Throw New InvalidOperationException("Cardslist already contains twin cards.")
        End If

        'initialize the twin card
        'add to the collection
        card.CreateTwinCard()
        CardsList.Add(card.TwinCard)
      Next
    End Sub
#End Region

  End Class
End Namespace
